package de.eridea.learning.java.usermanagement.persistence;

import java.util.Collection;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;

public class UserManagerImpl implements UserManager {

	@PersistenceContext(unitName = "usermanagement")
	private EntityManager entityManager;

	public User getUser(String loginName) {
		User result;
		try {
			result = (User) (entityManager
					.createQuery(
							"from " + getClass().getPackage().getName()
									+ ".User u where u.loginName = :name")
					.setParameter("name", loginName).setMaxResults(1)
					.getSingleResult());
		} catch (NoResultException nre) {
			result = null;
		}
		return result;
	}

	public boolean nameExists(Class entity, String field, String value) {
		if (entity == null) {
			throw new IllegalArgumentException("Entity class must not be null.");
		}
		if (field == null) {
			throw new IllegalArgumentException("Field name must not be null.");
		}
		if (field.length() == 0) {
			throw new IllegalArgumentException(
					"Field name can not be empty string.");
		}
		if (value == null) {
			throw new IllegalArgumentException("Value must not be null.");
		}
		try {
			entity.getField(field);
		} catch (NoSuchFieldException nsf) {
			try {
				entity.getMethod(
						"get"
								+ field.substring(0, 1).toUpperCase(
										Locale.ENGLISH) + field.substring(1),
						null);
			} catch (NoSuchMethodException nsm) {
				throw new IllegalArgumentException("Field " + field
						+ " not found in " + entity.getCanonicalName());
			}
		}

		// /* How I'd like it to work: */
		// return (Boolean) entityManager
		// .createQuery(
		// "exists ( from " + entity.getCanonicalName()
		// + " e where e.:field = :value )")
		// .setParameter("field", field).setParameter("value", value)
		// .getSingleResult();

		return entityManager
				.createQuery(
						"from " + entity.getCanonicalName() + " e where e."
								+ field + " = :value )")
				.setParameter("value", value).setMaxResults(1).getResultList()
				.size() > 0;
	}

	public User createUserObject() {
		return new User();
	}

	public void persistUser(User user) {
		entityManager.persist(user);
	}

	public User createUser(String loginName, char[] password) {
		User user = new User();
		user.setLoginName(loginName);
		user.setPasswordHash(String.valueOf(password));
		// user.setPasswordVerificationData(verifier.digest(password));
		entityManager.persist(user);
		return null;
	}

	public Group createGroupObject() {
		return new Group();
	}

	public void persistGroup(Group group) {
		entityManager.persist(group);
	}

	public boolean addUserToGroup(String user, String group) {
		User userObj = getUser(user);
		Group groupObj = getGroup(group);
		if (userObj == null) {
			throw new NoResultException("No such user: " + user);
		}
		if (groupObj == null) {
			throw new NoResultException("No such group: " + group);
		}
		boolean done = false;
		if (userObj.getGroups().add(groupObj)) {
			if (groupObj.users.add(userObj)) {
				done = true;
			} else {
				throw new IllegalStateException(
						"Inconsistent object representation of user-group relationship.");
			}
		}
		return done;
	}

	public boolean removeUserFromGroup(String user, String group) {
		User userObj = getUser(user);
		Group groupObj = getGroup(group);
		if (userObj == null) {
			throw new NoResultException("No such user: " + user);
		}
		if (groupObj == null) {
			throw new NoResultException("No such group: " + group);
		}
		boolean done = false;
		if (userObj.getGroups().remove(groupObj)) {
			if (groupObj.users.remove(userObj)) {
				done = true;
			} else {
				throw new IllegalStateException(
						"Inconsistent object representation of user-group relationship.");
			}
		}
		return done;
	}

	public EntityManager getEntityManager() {
		return entityManager;
	}

	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	public void updateUser(User user) {
		entityManager.flush();

	}

	public Group getGroup(String groupName) {
		Group result;
		try {
			result = (Group) entityManager
					.createQuery(
							"from " + getClass().getPackage().getName()
									+ ".Group g where g.name = :name")
					.setParameter("name", groupName).setMaxResults(1)
					.getSingleResult();
		} catch (NoResultException nre) {
			result = null;
		}
		return result;
	}

	public void updateGroup(Group group) {
		entityManager.flush();
	}

	public Resource getResource(String resourceIdentifier) {
		Resource result;
		try {
			result = (Resource) entityManager
					.createQuery(
							"from " + getClass().getPackage().getName()
									+ ".Resource r where r.idString = :name")
					.setParameter("name", resourceIdentifier).setMaxResults(1)
					.getSingleResult();
		} catch (NoResultException nre) {
			result = null;
		}
		return result;
	}

	public void updateResource(Resource resource) {
		entityManager.flush();
	}

	public Resource createResourceObject() {
		return new Resource();
	}

	public void persistResource(Resource resource) {
		entityManager.persist(resource);
	}

	public boolean isUserInGroup(User user, Group group) {
		return isUserInGroup(user.getLoginName(), group.getName());
	}

	public boolean isUserInGroup(String userName, String groupName) {
		boolean result;
		result = (Boolean) entityManager
				.createQuery(
						"exists ( from "
								+ getClass().getPackage().getName()
								+ ".User u join u.groups g where u.loginName = :loginName and g.name = :groupName )")
				.setParameter("loginName", userName)
				.setParameter("groupName", groupName).getSingleResult();
		return result;
	}

	public boolean addUserToGroup(User user, Group group) {
		// TODO Auto-generated method stub
		return false;
	}

	public boolean removeUserFromGroup(User user, Group group) {
		// TODO Auto-generated method stub
		return false;
	}

	public boolean isPermittedGroupForResource(Group group, Resource resource) {
		// TODO Auto-generated method stub
		return false;
	}

	public boolean grantPermission(String group, String resource) {
		// TODO Auto-generated method stub
		return false;
	}

	public boolean revokePermission(String group, String resource) {
		// TODO Auto-generated method stub
		return false;
	}

	public boolean isUserInPermittedGroupForResource(String user,
			String resource) {
		// TODO Auto-generated method stub
		return false;
	}

	public Set<Group> getGroups() {
		return new HashSet<Group>((Collection<Group>) entityManager.createQuery("from "+Group.class.getCanonicalName()).getResultList());
	}

}
